<?php 
/**
 * @author Anh Nguyen
 * @copyright 2012
 */
 
 defined('APP') or die('Denied access !');
 
class Cache {
	protected $_cache_dir;

    function __construct()
	{
        $directory = SITE_PATH . DS . Core::$config['dir_cache'];
		$this->_cache_dir = new SplFileInfo($directory);
        if(! $this->_cache_dir->isDir()) {
		  $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE);
        }
	}
    
    private static function filename($string)
	{
		return sha1($string).'.cache';
	}
    
    private function _sanitize_id($id)
	{  
		// Change slashes and spaces to underscores
		return str_replace(array('/', '\\', ' '), '_', $id);
	}

	public function get($id, $default = NULL)
	{
		$filename = self::filename($this->_sanitize_id($id));
		$directory = $this->_resolve_directory($filename);
		$file = new SplFileInfo($directory.$filename);
		
        if ( ! $file->isFile())
		{
			return $default;
		} else {
    		$created  = $file->getMTime();
    		$data     = $file->openFile();
    		$lifetime = $data->fgets();
    		if ($data->eof())
    		{
    			die(__METHOD__.' corrupted cache file!');
    		}
    		$cache = '';
    		while ($data->eof() === FALSE)
    		{
    			$cache .= $data->fgets();
    		}
            
    		if (($created + (int) $lifetime) < time())
    		{
    			return $default;
    		}
    		else
    		{
    			return unserialize($cache);
    		}
		}
	}

	
	public function set($id, $data, $lifetime = NULL)
	{
		$filename = self::filename($this->_sanitize_id($id));
		$directory = $this->_resolve_directory($filename);
        
		if ($lifetime === NULL)
		{
			// Set to the default expiry
			$lifetime = Core::$config['cache_expire'];
		}

		$dir = new SplFileInfo($directory);

		if ( ! $dir->isDir())
		{
			if ( ! mkdir($directory, 0600, TRUE))
			{
				die('Cannot make directory cache !');
			}
			chmod($directory, 0777);
		}
        
		$resouce = new SplFileInfo($directory.$filename);
		$file = $resouce->openFile('w');
		$data = $lifetime."\n".serialize($data);
		$file->fwrite($data, strlen($data));
		return (bool) $file->fflush();
	}


	public function delete($id)
	{
		$filename = self::filename($this->_sanitize_id($id));
		$directory = $this->_resolve_directory($filename);

		return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE);
	}


	public function delete_all()
	{
		return $this->_delete_file($this->_cache_dir, TRUE);
	}

	public function garbage_collect()
	{
		$this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE);
		return;
	}

	private function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE)
	{
			if ($file->isFile())
			{
				if (is_array($file->getFilename()))
				{
					$delete = FALSE;
				} elseif ($only_expired === FALSE) {
					$delete = TRUE;
				} else {
					// Assess the file expiry to flag it for deletion
					$json = $file->openFile('w')->current();
					$data = json_decode($json);
					$delete = $data->expiry < time();
				}

				if ($delete === TRUE) {
				    chmod($file->getRealPath(),0666);
                    echo fileperms($file->getRealPath());
					return unlink($file->getRealPath());
                }
				else
					return FALSE;
			}
			elseif ($file->isDir())
			{
				$files = new DirectoryIterator($file->getPathname());

				// Iterate over each entry
				while ($files->valid())
				{
					// Extract the entry name
					$name = $files->getFilename();

					// If the name is not a dot
					if ($name != '.' AND $name != '..')
					{
						// Create new file resource
						$fp = new SplFileInfo($files->getRealPath());
						// Delete the file
						$this->_delete_file($fp);
					}

					// Move the file pointer on
					$files->next();
				}

				// If set to retain parent directory, return now
				if ($retain_parent_directory)
				{
					return TRUE;
				}
				
                unset($files);
				return rmdir($file->getRealPath());
			}
			else
			{
				// We get here if a file has already been deleted
				return FALSE;
			}
	}

	protected function _resolve_directory($filename)
	{
		return $this->_cache_dir->getRealPath().DS;
	}

	protected function _make_directory($directory, $mode = 0600, $recursive = FALSE, $context = NULL)
	{
		if ( ! mkdir($directory, $mode, $recursive))
		{
			die('Failed to create the defined cache directory cache !');
		}
		chmod($directory, $mode);
		return new SplFileInfo($directory);;
	}
}
